//
// Created by 吴喆 on 2020/3/4.
//

#ifndef SUMMER_SHADER_H
#define SUMMER_SHADER_H

#import <string>

#import <glad/glad.h>
#import <glm/glm.hpp>
#import <glm/ext.hpp>

class Shader {
public:
    // State
    GLuint id;

    // Constructor
    Shader():id(0) {};

    // Sets the current shader as active
    Shader &use();
    void disuse();

    // Compiles the shader from given source code
    void compile(const GLchar *vertexSource, const GLchar *fragmentSource,
                 const GLchar *geometrySource = nullptr); // Note: geometry source code is optional
    // Utility functions
    inline void setFloat(const GLchar *name, GLfloat value, GLboolean useShader = false) {
        if (useShader)
            this->use();
        glUniform1f(glGetUniformLocation(this->id, name), value);
    }

    inline void setInteger(const GLchar *name, GLint value, GLboolean useShader = false) {
        if (useShader)
            this->use();
        glUniform1i(glGetUniformLocation(this->id, name), value);
    }

    inline void setVector2f(const GLchar *name, GLfloat x, GLfloat y, GLboolean useShader = false) {
        if (useShader)
            this->use();
        glUniform2f(glGetUniformLocation(this->id, name), x, y);
    }

    inline void setVector2f(const GLchar *name, const glm::vec2 &value, GLboolean useShader = false) {
        if (useShader)
            this->use();
        glUniform2f(glGetUniformLocation(this->id, name), value.x, value.y);
    }

    inline void setVector3f(const GLchar *name, GLfloat x, GLfloat y, GLfloat z, GLboolean useShader = false) {
        if (useShader)
            this->use();
        glUniform3f(glGetUniformLocation(this->id, name), x, y, z);
    }

    inline void setVector3f(const GLchar *name, const glm::vec3 &value, GLboolean useShader = false) {
        if (useShader)
            this->use();
        glUniform3f(glGetUniformLocation(this->id, name), value.x, value.y, value.z);
    }

    inline void setVector4f(const GLchar *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLboolean useShader = false) {
        if (useShader)
            this->use();
        glUniform4f(glGetUniformLocation(this->id, name), x, y, z, w);
    }

    inline void setVector4f(const GLchar *name, const glm::vec4 &value, GLboolean useShader = false) {
        if (useShader)
            this->use();
        glUniform4f(glGetUniformLocation(this->id, name), value.x, value.y, value.z, value.w);
    }

    inline void setMatrix4(const GLchar *name, const glm::mat4 &matrix, GLboolean useShader = false) {
        if (useShader)
            this->use();
        glUniformMatrix4fv(glGetUniformLocation(this->id, name), 1, GL_FALSE, glm::value_ptr(matrix));
    }

private:
    // Checks if compilation or linking failed and if so, print the error logs
    inline void checkCompileErrors(GLuint object, std::string type);
};

#endif //SUMMER_SHADER_H
